<template>
  <div id="container">
    <div id="editor-box">
      <canvas id="three-canvas"></canvas>
    </div>
    <div class="editor">
      <input type="file" id="3DFileInput" accept=".fbx,.obj,.mtl" placeholder="模型导入" />
      <button @click="handleClick">插入【CSS2D元素】</button>
      <button @click="handleRemove">移除指定【绿色几何体】</button>
      <button @click="handleAddVueAPP('2d')">插入VueAPP【2D】</button>
      <button @click="handleAddVueAPP('3d')">插入VueAPP【3D】</button>
      <button @click="handleAnimation">相机focus选中物品</button>
      <button @click="handleValidateSelectedFlow">组合流程验证</button>
      <button @click="handleHeatMap">插入【热力图】</button>
      <button @click="handleSwitchLight">开关灯</button>
      <button @click="handleChangeRainCount('light')">模拟【小雨】</button>
      <button @click="handleChangeRainCount('heavy')">模拟【大雨】</button>
    </div>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import Editor from '@/utils/Editor'
import * as THREE from 'three'
import { createCameraTween } from '@/editor/tools/useTween'
import { createHeatMapMesh, createSkyTextureAndBox } from '@/utils/Editor/tools/extraMaterials'
import * as MOCK from '@/utils/Editor/mock'
import { createVueElement } from '@/utils/Editor/Manager/CSSManager'
import { createRainObject } from '@/utils/Editor/tools/particle/useRain'
import { createMesh } from '@/utils/Editor/tools/particle/useWaterSplash'

/**
 * @type {Editor | null}
 */
let editor = null

const createTestDiv = () => {
  const div = document.createElement('div')
  div.style.pointerEvents = 'auto'
  div.className = 'test-div'
  div.textContent = 'Hello, CSS2D!'
  div.onclick = () => {
    ElMessage({
      message: 'CSS2DClicked'
    })
  }
  return div
}

const handleClick = () => {
  const element = createTestDiv()
  editor.cssManager.insertCss2DObject({ element })
}

const handleRemove = () => {
  cubs.forEach((cube) => {
    const { transformControls } = editor.controls
    // 移除前应该先取消选中
    transformControls.object === cube && transformControls.detach()
    editor.scene.remove(cube, true)
  })
}

const handleAddVueAPP = (type) => {
  const { element } = createVueElement('container', true)
  switch (type) {
    case '2d': {
      editor.cssManager.insertCss2DObject({ element })
    } break;
    case '3d': {
      editor.cssManager.insertCss3DObject({
        element, scale: 0.2,
        position: {
          x: 80,
          y: 20,
          z: 0
        }
      })
    } break;
  }
}

// 通过按钮触发飞入动画
const handleAnimation = () => {
  const { camera, controls } = editor
  if (controls.transformControls.object) {
    // const target0 = new THREE.Vector3(0, 0, 0);
    // const selectedObj = editor.outlinePass.selectedObjects[0]
    const selectedObj = controls.transformControls.object
    const selectdWorldPosition = new THREE.Vector3();
    selectedObj.getWorldPosition(selectdWorldPosition);
    const endWorldPosition = selectdWorldPosition.clone().addScalar(20)
    createCameraTween({ camera, controls: controls.orbitControls, endPos: endWorldPosition, endTarget: selectdWorldPosition, duration: 1000 })
  } else {
    ElMessage({
      message: '请先选中物体'
    })
  }
  // FIXME: 原始判断方法
  // if (editor.outlinePass.selectedObjects.length > 0) {

  // }
}


/**
 * 实现一个组合功能验证
 *  - 创建一个CSS3D
 *  - 为该3D绑定点击事件，当click时
 *    - 完成飞入动画
 *    - 在其旁边创建一个【数据卡片】
 *  - 再次点击时，隐藏【数据卡片】
 */
const handleValidateSelectedFlow = () => {
  const element = document.createElement('div')
  element.style.pointerEvents = 'auto'
  element.className = 'test-marker-warning'
  const mesh = editor.cssManager.insertCss3DObject({ element, position: { x: -20, y: 5, z: 10 }, scale: 0.05 })
  let vmInstance = null
  element.addEventListener('click', function () {

    // 相机跟随
    const { camera, controls } = editor
    const meshWorldPosition = new THREE.Vector3();
    mesh.getWorldPosition(meshWorldPosition);

    const endWorldPosition = meshWorldPosition.clone().addScalar(20)
    endWorldPosition.y -= 2
    endWorldPosition.z += 5
    endWorldPosition.x += 2
    createCameraTween({ camera, controls: controls.orbitControls, endPos: endWorldPosition, endTarget: meshWorldPosition, duration: 1000 })

    // 闭包弹出vue卡片
    if (vmInstance) {
      typeof vmInstance.handleClose === 'function' && vmInstance.handleClose()
      vmInstance = null
    } else {
      let { element, vm } = createVueElement('container')
      vmInstance = vm
      const cardObject = editor.cssManager.insertCss3DObject({ element, scale: 0.05 })
      cardObject.position.set(meshWorldPosition.x + 10, meshWorldPosition.y + 2, meshWorldPosition.z)
    }
  })
}

let heatWidth = 10
let heatHeight = 10

const mockHeatData = [
  [0, 0, 5], [.7, .8, 6], [.6, .3, 8],
  [.3, .4, 3], [.4, .5, 2], [.5, .6, 4],
  [.25, .25, 9.5], [1, 1, 7]]
const handleHeatMap = () => {
  const mesh = createHeatMapMesh(
    {
      width: heatWidth,
      height: heatHeight,
      data: mockHeatData,
    }
  )
  mesh.position.set(heatWidth, 1, 0)
  mesh.rotateX(-Math.PI / 2)
  editor.scene.add(mesh)
}

/**
 * 模拟开关灯流程验证
 */
let originMaterial = null
let lightObj = null
let lampSpotLight = null
const handleValidateSwitchLampFlow = async () => {
  const { scene, renderer } = editor
  // 启用阴影渲染
  renderer.shadowMap.enabled = true;
  // renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  // // 创建一个1000*1000的灰色mesh
  const geometry = new THREE.PlaneGeometry(500, 500);
  const material = new THREE.MeshStandardMaterial({ color: 0x808080 });
  const plane = new THREE.Mesh(geometry, material);
  plane.receiveShadow = true;
  plane.rotation.x = -Math.PI / 2;
  scene.add(plane);

  // 加载一个lamp模型
  await editor.load({ url: '3D/lamp_allinone.fbx', scale: 0.5 }, (lamp) => {
    const materialName = 'dengpao'
    lamp.traverse((obj) => {
      if (obj.isMesh) {
        console.log('原始layers-mask', obj.layers.mask)
        obj.layers.enable(1)
        console.log('修改后的layers-mask', obj.layers.mask)
        if (Array.isArray(obj.material)) {
          obj.material.forEach((material, index) => {
            if (material.name === materialName) {
              if (!originMaterial) originMaterial = material
              if (!lightObj) lightObj = obj
            }
          });
          console.log(lightObj, originMaterial)
        } else {
          if (obj.material.name === materialName) {
            if (!originMaterial) originMaterial = material
            if (!lightObj) lightObj = obj
          }
        }
      }
    });
    lamp.position.set(0, 50, 0)
    lamp.castShadow = true;
    lamp.layers.enable(1)
    scene.add(lamp)
  })

  // // 创建一个点光源
  const spotLight = new THREE.SpotLight(0xffd700, 1.0);
  // const spotLightHelper = new THREE.SpotLightHelper(spotLight, 0xffffff)
  spotLight.angle = Math.PI / 4; // 聚光灯的角度，控制光束的宽度
  spotLight.decay = 0.5;
  spotLight.intensity = 0
  spotLight.position.set(0, 20, 0);
  scene.add(spotLight)
  // scene.add(spotLightHelper)
  lampSpotLight = spotLight
}

/**
 * 模拟改变下雨强度
 */
const handleChangeRainCount = (type) => {
  const typeToCount = {
    'light': 1000,
    'heavy': 20000,
  }
  rain && rain.updateRainCount(typeToCount[type] || 5000)
}

/**
 * 模拟开关灯
 */
const handleSwitchLight = () => {
  if (!lightObj) return
  const { bloomEffect } = editor
  const materialName = 'dengpao'
  let materialIndex = lightObj.material.findIndex((_material) => _material.name === materialName)
  if (bloomEffect.selection.has(lightObj)) {
    // 当前是开灯状态，需要归还material
    lightObj.material[materialIndex] = originMaterial
    lampSpotLight && (lampSpotLight.intensity = 0)
    handleSwitchDayNight('day')
  } else {
    // 当前是关灯状态，需要修改material，注意要保留原来的materialName
    const newMaterial = new THREE.MeshBasicMaterial({ color: 0xffd700, name: materialName });
    lightObj.material[materialIndex] = newMaterial;
    lampSpotLight && (lampSpotLight.intensity = 10)
    handleSwitchDayNight('night')
  }
  bloomEffect.selection.toggle(lightObj)
}

const handleSwitchDayNight = (type) => {
  const { light } = editor
  light.switchDayNight(type)
}

let cubs = []

/**
 * @type{import('@/utils/Editor/tools/particle/useRain').Rain}
 */
let rain = null

onMounted(async () => {

  editor = new Editor({
    canvasId: 'three-canvas', containerId: 'editor-box'
  })
  editor.animate()
  const { controls, scene, renderer, camera, bloomEffect } = editor
  renderer.shadowMap.enabled = true;
  // const { textureCube } = createSkyTextureAndBox(scene)
  // // 设置场景背景
  // scene.background = textureCube

  // 创建一个立方体

  const cube = MOCK.createGeometry()
  cube.position.set(15, 10, 0)
  cube.castShadow = true;
  scene.add(cube, true)
  // bloomEffect.selection.toggle(cube)
  cubs.push(cube)

  controls.orbitControls.addEventListener('change', () => {
    renderer.render(scene, camera)
    rain && rain.position.set(camera.position.x, camera.position.y - scale / 2, camera.position.z)
  })

  // const cube2 = MOCK.createGeometry()
  // // cube2.position.set(10, 0, 0)
  // // scene.add(cube2)
  // cubs.push(cub2)

  // 验证下雨效果
  const scale = 10
  const { rain: _rain, renderRain } = createRainObject({
    renderer,
    maxHeight: scale,
    boxSize: new THREE.Vector3(scale, scale, scale),
    rainCount: 5000
  })
  rain = _rain
  // 将相机位置设置为rain的位置
  _rain.position.set(camera.position.x, camera.position.y - scale / 2, camera.position.z)
  scene.add(rain)

  renderer.setAnimationLoop(() => {
    renderRain({ renderer, scene })
  })

  await editor.load({ url: '3D/building.fbx', scale: 0.05 }, (building) => {
    // X方向拉伸20%
    building.scale.set(1.5, 1, 1)
    const boundingBox = new THREE.Box3().setFromObject(building);

    const size = boundingBox.getSize(new THREE.Vector3());
    const { x, y, z } = size
    // 开启模型的投影
    console.log('buiding', building)
    building.castShadow = true;
    building.position.set(-x / 2, 0, z / 2);
    scene.add(building)

    heatWidth = x
    heatHeight = z
    // 创建一个热力图
    // // const mesh = createHeatMapMesh
    // const mesh = createHeatMapMesh({
    //   width: x, height: z, data: [
    //     [0, 0, 5], [.7, .8, 6], [.6, .3, 8],
    //     [.3, .4, 3], [.4, .5, 2], [.5, .6, 4],
    //     [.25, .25, 9.5], [1, 1, 7]]
    // })
    // mesh.rotateX(-Math.PI / 2)
    // mesh.position.set(0, 1, 0)
    // scene.add(mesh)
  })

  // await editor.load({ url: '3D/carrot.fbx', scale: 1 }, (carrot) => {
  //   carrot.position.set(20, 0, 0)
  //   scene.add(carrot, true)
  // })
  // await editor.load({ url: '3D/GasSensor.obj', scale: 0.05 }, (model) => {
  //   model.position.set(-20, 5, 0)
  //   model.rotateX(Math.PI / 2)
  //   scene.add(model, true)
  // })


  document.getElementById('3DFileInput').addEventListener('change', async (e) => {
    const file = e.target.files[0]
    console.log(file)
    editor.load({ file, scale: 0.5 }, (object3D) => {
      scene.add(object3D, true)
      renderer.render(scene, camera)
    })
  })

  // 喷水效果验证
  await editor.load({ url: '3D/waterValve.fbx', scale: 0.3 }, (model) => {
    model.castShadow = true;
    model.position.set(10, 10, 0)
    model.rotateY(Math.PI / 2)
    scene.add(model, true)
    // 喷水
    model.rotateY(Math.PI)
    const mesh = createMesh(scene)
    mesh.position.set(4.5, 6.5, 0)
    // mesh.rotateZ(Math.PI / 2)
    scene.add(mesh)
    function animate() {
      mesh.render()
      requestAnimationFrame(animate);
    }
    animate()
  })

  // 初始化开关灯验证
  handleValidateSwitchLampFlow()
  renderer.render(scene, camera)
})
</script>

<style lang="scss">
#container {
  $editorWidth: 20vw;
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;


  $editorWidth: 85vw;

  #editor-box {
    width: $editorWidth ;
    height: 100vh;
    position: relative;
    background-color: black;

    #three-canvas {
      width: 100%;
      height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      z-index: 1;
    }
  }

  .editor {
    width: calc(100vw - #{$editorWidth});
    height: 100vh;
    background-color: whitesmoke;
    display: flex;
    flex-direction: column;

    button {
      width: 50%;
      margin-bottom: 10px;
    }
  }

  .test-div {
    width: 50px;
    height: 50px;
    background-color: red;
  }

  .test-marker-warning {
    background-image: url('/static/marker_warning.png');
    background-size: cover;
    background-repeat: no-repeat;
    $scale: 1;
    width: calc(44px * #{$scale});
    height: calc(92px * #{$scale});
    cursor: pointer;
  }
}
</style>
